<?php

/*
 * This script belongs to Aspect PHP.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This script is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-
 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 */

/**
 * The aspectProxy intercepts all method calls to add before, around and after
 * advices
 *
 * @package    aspectPhp
 * @subpackage lib
 * @author     gboisse
 */
class aspectProxy {

    /** @var mixed The targeted class instance */
    protected $proxifiedClass;
    /** @var string The name of the targeted class */
    protected $proxifiedClassName;

    /**
     * Initializes the proxy with the given targeted instance
     *
     * @param mixed $proxifiedClass The targeted class instance
     */
    public function __construct($proxifiedClass) {

        $this->proxifiedClass = $proxifiedClass;
        $this->proxifiedClassName = get_class($proxifiedClass);
    }

    /**
     * Magic function that intercepts any call to methods to allow advices to
     * be called
     *
     * @param String $name The method name
     * @param mixed $arguments Arguments to pass to the method
     */
    public function __call($name, $arguments) {
        $joinpoint = new aspectJoinpointImpl($this->proxifiedClassName, $name, $arguments);

        $this->runAdvices($joinpoint, aspectContainer::getInstance()->getBeforePointcuts());

        $adviceChain = new aspectAdviceChain(aspectContainer::getInstance()->getAroundPointcuts());
        try {
            $joinpoint->setResult($adviceChain->proceed($joinpoint));
        } catch (Exception $exception) {
            $joinpoint->setException($exception);

            if (!$this->runAdvices($joinpoint, aspectContainer::getInstance()->getAfterThrowingPointcuts())) {
                throw $exception;
            }
        }

        if (!$joinpoint->hasException()) {
            $this->runAdvices($joinpoint, aspectContainer::getInstance()->getAfterReturningPointcuts());
        }

        return $joinpoint->getResult();
    }

    /**
     * Runs the advices that match the method name from the class name
     *
     * @param aspectJoinpoint $joinpoint The current joinpoint
     * @param array $pointcuts The pointcuts to match
     * @return boolean false if no advice was executed
     */
    protected function runAdvices(aspectJoinpoint $joinpoint, $pointcuts) {

        $result = false;
        foreach ($pointcuts as $pointcut) {
            if ($pointcut->matches($joinpoint)) {
                $result = true;
                aspectUtils::callFunction(
                                array(
                                    aspectContainer::getInstance()->getAspectClassInstance($pointcut->getAspectClassName()),
                                    $pointcut->getAdviceMethodName()
                                ),
                                array($joinpoint));
            }
        }

        return $result;
    }

}
